2 using System
.Collections
.Generic
;
3 using UnityEngine
.Tilemaps
;
7 public class RuleTile
<T
> : RuleTile
9 public sealed override Type m_NeighborType { get { return typeof(T); }
}
13 public class RuleTile
: TileBase
16 private const string s_XIconString
= "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAABoSURBVDhPnY3BDcAgDAOZhS14dP1O0x2C/LBEgiNSHvfwyZabmV0jZRUpq2zi6f0DJwdcQOEdwwDLypF0zHLMa9+NQRxkQ+ACOT2STVw/q8eY1346ZlE54sYAhVhSDrjwFymrSFnD2gTZpls2OvFUHAAAAABJRU5ErkJggg==";
17 private const string s_Arrow0
= "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAACYSURBVDhPzZExDoQwDATzE4oU4QXXcgUFj+YxtETwgpMwXuFcwMFSRMVKKwzZcWzhiMg91jtg34XIntkre5EaT7yjjhI9pOD5Mw5k2X/DdUwFr3cQ7Pu23E/BiwXyWSOxrNqx+ewnsayam5OLBtbOGPUM/r93YZL4/dhpR/amwByGFBz170gNChA6w5bQQMqramBTgJ+Z3A58WuWejPCaHQAAAABJRU5ErkJggg==";
18 private const string s_Arrow1
= "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAABqSURBVDhPxYzBDYAgEATpxYcd+PVr0fZ2siZrjmMhFz6STIiDs8XMlpEyi5RkO/d66TcgJUB43JfNBqRkSEYDnYjhbKD5GIUkDqRDwoH3+NgTAw+bL/aoOP4DOgH+iwECEt+IlFmkzGHlAYKAWF9R8zUnAAAAAElFTkSuQmCC";
19 private const string s_Arrow2
= "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAAC0SURBVDhPjVE5EsIwDMxPKFKYF9CagoJH8xhaMskLmEGsjOSRkBzYmU2s9a58TUQUmCH1BWEHweuKP+D8tphrWcAHuIGrjPnPNY8X2+DzEWE+FzrdrkNyg2YGNNfRGlyOaZDJOxBrDhgOowaYW8UW0Vau5ZkFmXbbDr+CzOHKmLinAXMEePyZ9dZkZR+s5QX2O8DY3zZ/sgYcdDqeEVp8516o0QQV1qeMwg6C91toYoLoo+kNt/tpKQEVvFQAAAAASUVORK5CYII=";
20 private const string s_Arrow3
= "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAAB2SURBVDhPzY1LCoAwEEPnLi48gW5d6p31bH5SMhp0Cq0g+CCLxrzRPqMZ2pRqKG4IqzJc7JepTlbRZXYpWTg4RZE1XAso8VHFKNhQuTjKtZvHUNCEMogO4K3BhvMn9wP4EzoPZ3n0AGTW5fiBVzLAAYTP32C2Ay3agtu9V/9PAAAAAElFTkSuQmCC";
21 private const string s_Arrow5
= "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAABqSURBVDhPnY3BCYBADASvFx924NevRdvbyoLBmNuDJQMDGjNxAFhK1DyUQ9fvobCdO+j7+sOKj/uSB+xYHZAxl7IR1wNTXJeVcaAVU+614uWfCT9mVUhknMlxDokd15BYsQrJFHeUQ0+MB5ErsPi/6hO1AAAAAElFTkSuQmCC";
22 private const string s_Arrow6
= "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAACaSURBVDhPxZExEkAwEEVzE4UiTqClUDi0w2hlOIEZsV82xCZmQuPPfFn8t1mirLWf7S5flQOXjd64vCuEKWTKVt+6AayH3tIa7yLg6Qh2FcKFB72jBgJeziA1CMHzeaNHjkfwnAK86f3KUafU2ClHIJSzs/8HHLv09M3SaMCxS7ljw/IYJWzQABOQZ66x4h614ahTCL/WT7BSO51b5Z5hSx88AAAAAElFTkSuQmCC";
23 private const string s_Arrow7
= "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAABQSURBVDhPYxh8QNle/T8U/4MKEQdAmsz2eICx6W530gygr2aQBmSMphkZYxqErAEXxusKfAYQ7XyyNMIAsgEkaYQBkAFkaYQBsjXSGDAwAAD193z4luKPrAAAAABJRU5ErkJggg==";
24 private const string s_Arrow8
= "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAACYSURBVDhPxZE9DoAwCIW9iUOHegJXHRw8tIdx1egJTMSHAeMPaHSR5KVQ+KCkCRF91mdz4VDEWVzXTBgg5U1N5wahjHzXS3iFFVRxAygNVaZxJ6VHGIl2D6oUXP0ijlJuTp724FnID1Lq7uw2QM5+thoKth0N+GGyA7IA3+yM77Ag1e2zkey5gCdAg/h8csy+/89v7E+YkgUntOWeVt2SfAAAAABJRU5ErkJggg==";
26 private static Texture2D
[] s_Arrows
;
27 public static Texture2D
[] arrows
33 s_Arrows
= new Texture2D
[10];
34 s_Arrows
[0] = Base64ToTexture(s_Arrow0
);
35 s_Arrows
[1] = Base64ToTexture(s_Arrow1
);
36 s_Arrows
[2] = Base64ToTexture(s_Arrow2
);
37 s_Arrows
[3] = Base64ToTexture(s_Arrow3
);
38 s_Arrows
[5] = Base64ToTexture(s_Arrow5
);
39 s_Arrows
[6] = Base64ToTexture(s_Arrow6
);
40 s_Arrows
[7] = Base64ToTexture(s_Arrow7
);
41 s_Arrows
[8] = Base64ToTexture(s_Arrow8
);
42 s_Arrows
[9] = Base64ToTexture(s_XIconString
);
48 public static Texture2D
Base64ToTexture(string base64
)
50 Texture2D t
= new Texture2D(1, 1);
51 t
.hideFlags
= HideFlags
.HideAndDontSave
;
52 t
.LoadImage(System
.Convert
.FromBase64String(base64
));
56 public virtual void RuleOnGUI(Rect rect
, Vector2Int pos
, int neighbor
)
60 case RuleTile
.TilingRule
.Neighbor
.DontCare
:
62 case RuleTile
.TilingRule
.Neighbor
.This
:
63 GUI
.DrawTexture(rect
, arrows
[pos
.y
* 3 + pos
.x
]);
65 case RuleTile
.TilingRule
.Neighbor
.NotThis
:
66 GUI
.DrawTexture(rect
, arrows
[9]);
69 var style
= new GUIStyle();
70 style
.alignment
= TextAnchor
.MiddleCenter
;
72 GUI
.Label(rect
, neighbor
.ToString(), style
);
75 var allConsts
= m_NeighborType
.GetFields(System
.Reflection
.BindingFlags
.Public
| System
.Reflection
.BindingFlags
.Static
| System
.Reflection
.BindingFlags
.FlattenHierarchy
);
76 foreach (var c
in allConsts
)
78 if ((int)c
.GetValue(null) == neighbor
)
80 GUI
.Label(rect
, new GUIContent("", c
.Name
));
87 public virtual Type m_NeighborType { get { return typeof(TilingRule.Neighbor); }
}
89 private static readonly int[,] RotatedOrMirroredIndexes
=
91 {2, 4, 7, 1, 6, 0, 3, 5}
, // 90
92 {7, 6, 5, 4, 3, 2, 1, 0}
, // 180, XY
93 {5, 3, 0, 6, 1, 7, 4, 2}
, // 270
94 {2, 1, 0, 4, 3, 7, 6, 5}
, // X
95 {5, 6, 7, 3, 4, 0, 1, 2}
, // Y
97 private static readonly int NeighborCount
= 8;
99 public Sprite m_DefaultSprite
;
100 public Tile
.ColliderType m_DefaultColliderType
= Tile
.ColliderType
.Sprite
;
101 public TileBase m_Self
103 get { return m_OverrideSelf ? m_OverrideSelf : this; }
104 set { m_OverrideSelf = value; }
107 private TileBase
[] m_CachedNeighboringTiles
= new TileBase
[NeighborCount
];
108 private TileBase m_OverrideSelf
;
111 public class TilingRule
113 public int[] m_Neighbors
;
114 public Sprite
[] m_Sprites
;
115 public float m_AnimationSpeed
;
116 public float m_PerlinScale
;
117 public Transform m_RuleTransform
;
118 public OutputSprite m_Output
;
119 public Tile
.ColliderType m_ColliderType
;
120 public Transform m_RandomTransform
;
124 m_Output
= OutputSprite
.Single
;
125 m_Neighbors
= new int[NeighborCount
];
126 m_Sprites
= new Sprite
[1];
127 m_AnimationSpeed
= 1f
;
128 m_PerlinScale
= 0.5f
;
129 m_ColliderType
= Tile
.ColliderType
.Sprite
;
131 for (int i
= 0; i
< m_Neighbors
.Length
; i
++)
132 m_Neighbors
[i
] = Neighbor
.DontCare
;
135 public class Neighbor
137 public const int DontCare
= 0;
138 public const int This
= 1;
139 public const int NotThis
= 2;
141 public enum Transform { Fixed, Rotated, MirrorX, MirrorY }
142 public enum OutputSprite { Single, Random, Animation }
145 [HideInInspector
] public List
<TilingRule
> m_TilingRules
;
147 public override void GetTileData(Vector3Int position
, ITilemap tilemap
, ref TileData tileData
)
149 TileBase
[] neighboringTiles
= null;
150 GetMatchingNeighboringTiles(tilemap
, position
, ref neighboringTiles
);
151 var iden
= Matrix4x4
.identity
;
153 tileData
.sprite
= m_DefaultSprite
;
154 tileData
.colliderType
= m_DefaultColliderType
;
155 tileData
.flags
= TileFlags
.LockTransform
;
156 tileData
.transform
= iden
;
158 foreach (TilingRule rule
in m_TilingRules
)
160 Matrix4x4 transform
= iden
;
161 if (RuleMatches(rule
, ref neighboringTiles
, ref transform
))
163 switch (rule
.m_Output
)
165 case TilingRule
.OutputSprite
.Single
:
166 case TilingRule
.OutputSprite
.Animation
:
167 tileData
.sprite
= rule
.m_Sprites
[0];
169 case TilingRule
.OutputSprite
.Random
:
170 int index
= Mathf
.Clamp(Mathf
.FloorToInt(GetPerlinValue(position
, rule
.m_PerlinScale
, 100000f
) * rule
.m_Sprites
.Length
), 0, rule
.m_Sprites
.Length
- 1);
171 tileData
.sprite
= rule
.m_Sprites
[index
];
172 if (rule
.m_RandomTransform
!= TilingRule
.Transform
.Fixed
)
173 transform
= ApplyRandomTransform(rule
.m_RandomTransform
, transform
, rule
.m_PerlinScale
, position
);
176 tileData
.transform
= transform
;
177 tileData
.colliderType
= rule
.m_ColliderType
;
183 private static float GetPerlinValue(Vector3Int position
, float scale
, float offset
)
185 return Mathf
.PerlinNoise((position
.x
+ offset
) * scale
, (position
.y
+ offset
) * scale
);
188 public override bool GetTileAnimationData(Vector3Int position
, ITilemap tilemap
, ref TileAnimationData tileAnimationData
)
190 TileBase
[] neighboringTiles
= null;
191 var iden
= Matrix4x4
.identity
;
192 foreach (TilingRule rule
in m_TilingRules
)
194 if (rule
.m_Output
== TilingRule
.OutputSprite
.Animation
)
196 Matrix4x4 transform
= iden
;
197 GetMatchingNeighboringTiles(tilemap
, position
, ref neighboringTiles
);
198 if (RuleMatches(rule
, ref neighboringTiles
, ref transform
))
200 tileAnimationData
.animatedSprites
= rule
.m_Sprites
;
201 tileAnimationData
.animationSpeed
= rule
.m_AnimationSpeed
;
209 public override void RefreshTile(Vector3Int location
, ITilemap tileMap
)
211 if (m_TilingRules
!= null && m_TilingRules
.Count
> 0)
213 for (int y
= -1; y
<= 1; y
++)
215 for (int x
= -1; x
<= 1; x
++)
217 base.RefreshTile(location
+ new Vector3Int(x
, y
, 0), tileMap
);
223 base.RefreshTile(location
, tileMap
);
227 public bool RuleMatches(TilingRule rule
, ref TileBase
[] neighboringTiles
, ref Matrix4x4 transform
)
229 // Check rule against rotations of 0, 90, 180, 270
230 for (int angle
= 0; angle
<= (rule
.m_RuleTransform
== TilingRule
.Transform
.Rotated
? 270 : 0); angle
+= 90)
232 if (RuleMatches(rule
, ref neighboringTiles
, angle
))
234 transform
= Matrix4x4
.TRS(Vector3
.zero
, Quaternion
.Euler(0f
, 0f
, -angle
), Vector3
.one
);
239 // Check rule against x-axis mirror
240 if ((rule
.m_RuleTransform
== TilingRule
.Transform
.MirrorX
) && RuleMatches(rule
, ref neighboringTiles
, true, false))
242 transform
= Matrix4x4
.TRS(Vector3
.zero
, Quaternion
.identity
, new Vector3(-1f
, 1f
, 1f
));
246 // Check rule against y-axis mirror
247 if ((rule
.m_RuleTransform
== TilingRule
.Transform
.MirrorY
) && RuleMatches(rule
, ref neighboringTiles
, false, true))
249 transform
= Matrix4x4
.TRS(Vector3
.zero
, Quaternion
.identity
, new Vector3(1f
, -1f
, 1f
));
256 private static Matrix4x4
ApplyRandomTransform(TilingRule
.Transform type
, Matrix4x4 original
, float perlinScale
, Vector3Int position
)
258 float perlin
= GetPerlinValue(position
, perlinScale
, 200000f
);
261 case TilingRule
.Transform
.MirrorX
:
262 return original
* Matrix4x4
.TRS(Vector3
.zero
, Quaternion
.identity
, new Vector3(perlin
< 0.5 ? 1f
: -1f
, 1f
, 1f
));
263 case TilingRule
.Transform
.MirrorY
:
264 return original
* Matrix4x4
.TRS(Vector3
.zero
, Quaternion
.identity
, new Vector3(1f
, perlin
< 0.5 ? 1f
: -1f
, 1f
));
265 case TilingRule
.Transform
.Rotated
:
266 int angle
= Mathf
.Clamp(Mathf
.FloorToInt(perlin
* 4), 0, 3) * 90;
267 return Matrix4x4
.TRS(Vector3
.zero
, Quaternion
.Euler(0f
, 0f
, -angle
), Vector3
.one
);
272 public virtual bool RuleMatch(int neighbor
, TileBase tile
)
276 case TilingRule
.Neighbor
.This
: return tile
== m_Self
;
277 case TilingRule
.Neighbor
.NotThis
: return tile
!= m_Self
;
282 public bool RuleMatches(TilingRule rule
, ref TileBase
[] neighboringTiles
, int angle
)
284 for (int i
= 0; i
< NeighborCount
; ++i
)
286 int index
= GetRotatedIndex(i
, angle
);
287 TileBase tile
= neighboringTiles
[index
];
288 if (!RuleMatch(rule
.m_Neighbors
[i
], tile
))
296 public bool RuleMatches(TilingRule rule
, ref TileBase
[] neighboringTiles
, bool mirrorX
, bool mirrorY
)
298 for (int i
= 0; i
< NeighborCount
; ++i
)
300 int index
= GetMirroredIndex(i
, mirrorX
, mirrorY
);
301 TileBase tile
= neighboringTiles
[index
];
302 if (!RuleMatch(rule
.m_Neighbors
[i
], tile
))
310 private void GetMatchingNeighboringTiles(ITilemap tilemap
, Vector3Int position
, ref TileBase
[] neighboringTiles
)
312 if (neighboringTiles
!= null)
315 if (m_CachedNeighboringTiles
== null || m_CachedNeighboringTiles
.Length
< NeighborCount
)
316 m_CachedNeighboringTiles
= new TileBase
[NeighborCount
];
319 for (int y
= 1; y
>= -1; y
--)
321 for (int x
= -1; x
<= 1; x
++)
323 if (x
!= 0 || y
!= 0)
325 Vector3Int tilePosition
= new Vector3Int(position
.x
+ x
, position
.y
+ y
, position
.z
);
326 m_CachedNeighboringTiles
[index
++] = tilemap
.GetTile(tilePosition
);
330 neighboringTiles
= m_CachedNeighboringTiles
;
333 private int GetRotatedIndex(int original
, int rotation
)
340 return RotatedOrMirroredIndexes
[0, original
];
342 return RotatedOrMirroredIndexes
[1, original
];
344 return RotatedOrMirroredIndexes
[2, original
];
349 private int GetMirroredIndex(int original
, bool mirrorX
, bool mirrorY
)
351 if (mirrorX
&& mirrorY
)
353 return RotatedOrMirroredIndexes
[1, original
];
357 return RotatedOrMirroredIndexes
[3, original
];
361 return RotatedOrMirroredIndexes
[4, original
];
366 private int GetIndexOfOffset(Vector3Int offset
)
368 int result
= offset
.x
+ 1 + (-offset
.y
+ 1) * 3;
374 public Vector3Int
GetRotatedPos(Vector3Int original
, int rotation
)
381 return new Vector3Int(-original
.y
, original
.x
, original
.z
);
383 return new Vector3Int(-original
.x
, -original
.y
, original
.z
);
385 return new Vector3Int(original
.y
, -original
.x
, original
.z
);
390 public Vector3Int
GetMirroredPos(Vector3Int original
, bool mirrorX
, bool mirrorY
)
392 return new Vector3Int(original
.x
* (mirrorX
? -1 : 1), original
.y
* (mirrorY
? -1 : 1), original
.z
);